package com.jadows.tdog.server.listener.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.jadows.tdog.configuration.SpringContextHolder;
import com.jadows.tdog.dto.UserInfo;
import com.jadows.tdog.enums.ServiceExceptionEnum;
import com.jadows.tdog.exception.ServiceException;
import com.jadows.tdog.message.TempReqWrapper;
import com.jadows.tdog.message.WSRespWrapper;
import com.jadows.tdog.message.WsReqWrapper;
import com.jadows.tdog.message.configuration.WSActionDefinitionRegister;
import com.jadows.tdog.message.details.AbstractMessage;
import com.jadows.tdog.properties.ChatServerProperties;
import com.jadows.tdog.server.codec.JsonWebSocketFrame;
import com.jadows.tdog.server.listener.ChannelEventListener;
import com.jadows.tdog.state.OnlineUserRegister;
import com.jadows.tdog.state.SimpleWSSessionUtil;
import com.jadows.tdog.util.IdUtils;
import com.jadows.tdog.util.NettyUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.util.Date;

/**
 * @author taojiacheng
 */
@RequiredArgsConstructor
@Slf4j
public class ChatChannelEventListenerImpl implements ChannelEventListener {

    final ChatServerProperties properties;
    final OnlineUserRegister onlineUserRegister;

    @Override
    public void onShakeHandComplete(ChannelHandlerContext ctx) {
        String memberId = NettyUtil.getAttr(ctx.channel(), NettyUtil.UID);
        UserInfo userInfo = new UserInfo().setMemberId(memberId);
        // 状态管理
        SimpleWSSessionUtil.addUidChannelMapping(userInfo, ctx.channel());
        log.info("用户: {} 上线", userInfo.getMemberId());
        // 在线用户注册表
        onlineUserRegister.register(userInfo.getMemberId());
    }


    /**
     * 消息到达事件
     *
     * @param ctx     ChannelHandlerContext
     * @param message 消息
     */
    @Override
    public void onMessageArrived(ChannelHandlerContext ctx, String message) {
        TempReqWrapper tempReqWrapper = JSON.to(TempReqWrapper.class, message);
        if (tempReqWrapper.getAction() == null) {
            ServiceExceptionEnum noAction = ServiceExceptionEnum.NO_ACTION;
            sendMsg(ctx.channel(), WSRespWrapper.fail(tempReqWrapper.getSeq(), noAction.getCode(), noAction.getMessage()));
            return;
        }
        try {
            WsReqWrapper<AbstractMessage> reqWrapper = new WsReqWrapper<>()
                    .setSeq(tempReqWrapper.getSeq())
                    .setAction(tempReqWrapper.getAction());
            WSActionDefinitionRegister register = SpringContextHolder.getBean(WSActionDefinitionRegister.class);
            WSActionDefinitionRegister.ActionDefinition definition = register.get(tempReqWrapper.getAction());
            if (definition == null) {
                ServiceExceptionEnum noAction = ServiceExceptionEnum.NO_ACTION;
                sendMsg(ctx.channel(), WSRespWrapper.fail(tempReqWrapper.getSeq(), noAction.getCode(), noAction.getMessage()));
                return;
            }
            JSONObject data = tempReqWrapper.getData();
            AbstractMessage abstractMessage = data.to(definition.genericParameterType());
            if (abstractMessage.getMessageId() == null) {
                abstractMessage.setMessageId(IdUtils.nextId());
            }
            abstractMessage.setFromUid(SimpleWSSessionUtil.getUserDto(ctx.channel()).getMemberId());
            abstractMessage.setSendTime(new Date());
            log.debug("收到消息: {}", JSON.toJSONString(abstractMessage));
            reqWrapper.setData(abstractMessage);
            reqWrapper.setChannel(ctx.channel());
            // todo 按用户纬度限流
            Object resp = definition.method().invoke(definition.bean(), reqWrapper);
            WSRespWrapper respWrapper = WSRespWrapper.ok(tempReqWrapper.getSeq(), resp);
            sendMsg(ctx.channel(), respWrapper);
        } catch (Exception e) {
            log.error("消息处理异常: {}", e.getMessage(), e);
            WSRespWrapper wsRespWrapper = getErrorWsRespWrapper(e, tempReqWrapper);
            sendMsg(ctx.channel(), wsRespWrapper);
        }
    }

    /**
     * 构建错误的 WSRespWrapper
     *
     * @param e              异常
     * @param tempReqWrapper 请求
     * @return WSRespWrapper
     */
    private WSRespWrapper getErrorWsRespWrapper(Exception e, TempReqWrapper tempReqWrapper) {
        WSRespWrapper wsRespWrapper = new WSRespWrapper(tempReqWrapper.getSeq());
        if (e instanceof InvocationTargetException ite && ite.getTargetException() instanceof ServiceException se) {
            Integer code = se.getCode();
            String msg = se.getMessage();
            wsRespWrapper.setCode(code);
            wsRespWrapper.setMsg(msg);
        } else {
            ServiceExceptionEnum unknownException = ServiceExceptionEnum.UNKNOWN_EXCEPTION;
            wsRespWrapper.setCode(unknownException.getCode());
            wsRespWrapper.setMsg(unknownException.getMessage());
        }
        return wsRespWrapper;
    }

    /**
     * 客户端连接建立时
     *
     * @param ctx ChannelHandlerContext
     */
    @Override
    public void onAdded(ChannelHandlerContext ctx) {
        // do nth

    }

    /**
     * 客户端掉线
     *
     * @param ctx ChannelHandlerContext
     */
    @Override
    public void onRemoved(ChannelHandlerContext ctx) {
        disconnect(ctx);
    }


    @Override
    public void onDisconnect(ChannelHandlerContext ctx) {
        disconnect(ctx);
    }

    protected void disconnect(ChannelHandlerContext ctx) {
        UserInfo userInfo = SimpleWSSessionUtil.getUserDto(ctx.channel());
        // 客户端离线
        SimpleWSSessionUtil.removeUidChannelMapping(ctx.channel());
        ctx.close();
        if (userInfo != null) {
            onlineUserRegister.unregister(userInfo.getMemberId());
        }
    }

    protected void sendMsg(Channel channel, WSRespWrapper respWrapper) {
        channel.writeAndFlush(new JsonWebSocketFrame(respWrapper));
    }

}
